<?php

/*
 * Copyright (C) 2010 Emanuele Colonnelli <emanuele.colonnelli@phpstruct.org>
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

namespace org\phpstruct\core;

/**
 * Description of InvocationQueue
 *
 * @author Emanuele Colonnelli <emanuele.colonnelli@phpstruct.org>
 */
class InvocationQueue {

    private $helperQueue = array();
    private $classQueue = array();
    private $filterQueue = array();
    private $errors = array();
    private static $instance;

    public function getFilterQueue() {
        return $this->filterQueue;
    }

    public function setFilterQueue($filterQueue) {
        $this->filterQueue = $filterQueue;
    }

    public function getHelperQueue() {
        return $this->helperQueue;
    }

    public function setHelperQueue($helperQueue) {
        $this->helperQueue = $helperQueue;
    }

    public function getClassQueue() {
        return $this->classQueue;
    }

    public function setClassQueue($classQueue) {
        $this->classQueue = $classQueue;
    }

    public function getErrors() {
        return $this->errors;
    }

    public function setErrors($errors) {
        $this->errors = $errors;
    }

    private function __construct() {
        self::$instance = $this;
    }

    /**
     * @return InvocationQueue
     */
    public static function getInstance() {
        return (self::$instance instanceof self) ? self::$instance : new self();
    }

    public function pushHelper(\org\phpstruct\core\dto\XMapHelper $helper) {
        array_push($this->helperQueue, $helper);
    }

    /**
     *
     * @return \org\phpstruct\core\dto\XMapHelper
     */
    public function popHelper() {
        return is_array($this->helperQueue) ? array_pop($this->helperQueue) : null;
    }

    public function pushClass(\org\phpstruct\core\dto\XMapClass $class) {
        array_push($this->classQueue, $class);
    }

    /**
     *
     * @return \org\phpstruct\core\dto\XMapClass
     */
    public function popClass() {
        return is_array($this->classQueue) ? array_pop($this->classQueue) : null;
    }

    public function invokeList() {
        while ($h = $this->popHelper()) {
            $this->invokeHelper($h);
        }

        while ($c = $this->popClass()) {
            $this->invokeClass($c);
        }
    }

    private function invokeHelper(\org\phpstruct\core\dto\XMapHelper $helper) {
        if ($helper instanceof dto\XMapHelper) {
            $errors = $this->paramsValidation($helper->getParams());

            if ($errors != 1) {
                $error = new dto\ErrorData(1, $errors, null, Error::ERROR_TYPE_ERROR);
                throw new \org\phpstruct\core\exceptions\StructException(\implode(',', $this->errors), Error::E_LOADER_PARAM_VALIDATION, null);
            }

            $hname = $helper->getName();
            $_chelper = new \ReflectionClass($hname);
            $ohelper = $_chelper->newInstance();
            $ret = $ohelper->invoke($helper->getParams());
        }
    }

    private function invokeClass(\org\phpstruct\core\dto\XMapClass $class) {
        if ($class instanceof dto\XMapClass) {
            $errors = $this->paramsValidation($class->getParams());

            if (count($errors) != true) {
                $objErr = new dto\ErrorData(Error::E_LOADER_PARAM_VALIDATION, $errors, null, null);
                throw new exceptions\StructException($objErr->getMessage(), $objErr->getCode(), null);
            }

            $method = $class->getName() . "::" . $class->getMethod();

            $obj = new \ReflectionClass($class->getName());

            $hasMethod = $obj->hasMethod($class->getMethod());

            if ($hasMethod) {
                $r = new \ReflectionMethod($class->getName(), $class->getMethod());
                $params = $class->getParams();
                $prep = $this->prepareParams($params);

                $r->invokeArgs(null, $prep);
            } else {
                //TRY WITH CALLSTATIC
                $r = new \ReflectionMethod($class->getName(), '__callStatic');
                $params = $class->getParams();
                $prep = $this->prepareParams($params);

                array_unshift($prep, $class->getMethod());

                $r->invokeArgs(null, $prep);
            }
        }
    }

    private function prepareParams($params) {
        if (\is_null($params))
            return array();

        $prepared = array();
        while ($param = array_shift($params)) {
            if ($param instanceof \org\phpstruct\core\dto\XMapParam) {
                if (isset($_REQUEST[$param->getName()])) {
                    $prepared[$param->getName()] = $_REQUEST[$param->getName()];
//                    $prepared[] = $_REQUEST[$param->getName()];
                }
            }
        }
        return $prepared;
    }

    private function paramsValidation($params) {
        if (is_null($params))
            return true;
        while ($param = array_shift($params)) {
            if (!$param instanceof \org\phpstruct\core\dto\XMapParam) {
                throw new org\phpstruct\core\exceptions\StructException(_("Invalid parameter (not XMapParam"), 0, NULL);
            }
            if ($param->getRequired() == 1) {
                if (!isset($_REQUEST[$param->getName()])) {
                    $message = \sprintf('Parameter %s is missing or unset', $param->getName());
                    array_push($this->errors, $message);
                }
            }
        }
        return \count($this->errors) > 0 ? false : true;
    }

}

